﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceProcess;

namespace PowerTriggersWcf
{
    [DataContract(Namespace = "http://PWTWcf")]
    internal class TaskService : Task
    {
        #region constructors
        private TaskService()
            : base(TaskTypes.Service, true)
        {
        }

        internal TaskService(bool assignDefaultProperties)
            : base(TaskTypes.Service, assignDefaultProperties)
        {
        }

        internal TaskService(ServiceController controller, int sequence)
            : this()
        {
            this.Description = controller.DisplayName;
            this.Sequence = sequence;
            this.Status = controller.Status.ToString();
            this.ID = controller.ServiceName;
            this.Category = string.Empty;
        }
        #endregion

        internal override bool PerformAction(HistoryHeader historyHeader)
        {
            bool success;

            try
            {
                TaskPropertyChoice action = this.GetSelectedAction();

                using (ServiceController controller = new ServiceController(this.ID))
                {
                    switch (action.ChoiceType)
                    {
                        case TaskPropertyChoiceTypes.Start:
                            {
                                if (controller.Status == ServiceControllerStatus.Running || controller.Status == ServiceControllerStatus.StartPending)
                                {
                                    success = true;
                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Informational, string.Format("Task to start service '{0}' is ignored because the service is already running", this.Description), false);
                                }
                                else if (WaitForServiceToStop(controller))
                                {
                                    controller.Start();
                                    success = WaitForServiceToStart(controller, true);
                                    if (success)
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Service '{0}' successfully started", this.Description), false);
                                    }
                                    else
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to start service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                    }
                                }
                                else
                                {
                                    success = WaitForServiceToStart(controller);
                                    if (success)
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Informational, "Service is already started. No action taken.", false);
                                    }
                                    else
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to start service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                    }
                                }
                            }
                            break;
                        case TaskPropertyChoiceTypes.Stop:
                            {
                                if (controller.Status == ServiceControllerStatus.Stopped || controller.Status == ServiceControllerStatus.StopPending)
                                {
                                    success = true;
                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Informational, string.Format("Task to stop service '{0}' is ignored because the service is already stopped", this.Description), false);
                                }
                                else if (WaitForServiceToStart(controller))
                                {
                                    controller.Stop();
                                    success = WaitForServiceToStop(controller, true);
                                    if (success)
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Service '{0}' successfully stopped", this.Description), false);
                                    }
                                    else
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to stop service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                    }
                                }
                                else
                                {
                                    success = WaitForServiceToStop(controller);
                                    if (success)
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Informational, string.Format("Task to stop service '{0}' is ignored because the service is already stopped", this.Description), false);
                                    }
                                    else
                                    {
                                        Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to stop service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                    }
                                }
                            }
                            break;
                        case TaskPropertyChoiceTypes.Restart:
                            {
                                // first, check to see if status is one we should wait for
                                switch (controller.Status)
                                {
                                    case ServiceControllerStatus.Running:
                                    case ServiceControllerStatus.Stopped:
                                        break; // do nothing
                                    default: // any other Status then wait to see if the status changes
                                        {
                                            WaitForServiceToStartOrStop(controller, true);
                                        }
                                        break;
                                }

                                // next, perform Start or Restart if possible
                                switch (controller.Status)
                                {
                                    case ServiceControllerStatus.Stopped:
                                        {
                                            //Service.AddHistoryStatic(HistorySeverityLevels.Informational, "Service status is is reported as Stopped so performing a Start instead of a Restart.", false);
                                            controller.Start();
                                            success = WaitForServiceToStart(controller, true);
                                            if (success)
                                            {
                                                Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Service '{0}' successfully restarted", this.Description), false);
                                            }
                                            else
                                            {
                                                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to restart service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                            }
                                        }
                                        break;
                                    case ServiceControllerStatus.Running:
                                        {
                                            controller.Stop();
                                            if (WaitForServiceToStop(controller, true))
                                            {
                                                controller.Start();
                                                success = WaitForServiceToStart(controller);
                                                if (success)
                                                {
                                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Service '{0}' successfully restarted", this.Description), false);
                                                }
                                                else
                                                {
                                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to restart service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                                }
                                            }
                                            else
                                            {
                                                success = false;
                                                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to restart service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                            }
                                        }
                                        break;
                                    default:
                                        {
                                            success = false;
                                            Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to restart service '{0}'. The service status is reported as '{1}'.", this.Description, controller.Status.ToString()), false);
                                        }
                                        break;
                                }
                            }
                            break;
                        default:
                            throw new NotSupportedException(string.Format("ServiceAction '{0}' is not a supported device action for device '{1}'", action.ChoiceType.ToString(), this.Description));
                    }
                }
            }
            catch (System.Exception ex)
            {
                string msg = string.Format("Error when attempting '{0}' on service '{1}'. Error={2}", this.GetSelectedAction().ChoiceType.ToString(), this.Description, ex.Message.Trim());
                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, msg, false);
                success = false;
            }

            return success;
        }

        private bool WaitForServiceToStart(ServiceController controller)
        {
            return WaitForServiceToStart(controller, false);
        }

        private bool WaitForServiceToStart(ServiceController controller, bool longTimeout)
        {
            if (controller.Status != ServiceControllerStatus.Running)
            {
                Service.SavePendingHistory();
            }

            int timeout = longTimeout ? Constants.ServiceLongTimeout : Constants.ServiceTimeout;
            controller.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, 0, timeout));

            return (controller.Status == ServiceControllerStatus.Running);
        }

        private bool WaitForServiceToStop(ServiceController controller)
        {
            return WaitForServiceToStop(controller, false);
        }

        private bool WaitForServiceToStop(ServiceController controller, bool longTimeout)
        {
            if (controller.Status != ServiceControllerStatus.Stopped)
            {
                Service.SavePendingHistory();
            }

            int timeout = longTimeout ? Constants.ServiceLongTimeout : Constants.ServiceTimeout;
            controller.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, 0, timeout));

            return (controller.Status == ServiceControllerStatus.Stopped);
        }

        private bool WaitForServiceToStartOrStop(ServiceController controller, bool longTimeout)
        {
            int timeout = longTimeout ? Constants.ServiceLongTimeout : Constants.ServiceTimeout;
            long waitStarted = DateTime.UtcNow.Ticks;

            controller.Refresh();
            while ((controller.Status != ServiceControllerStatus.Running) && (controller.Status != ServiceControllerStatus.Stopped) && ((DateTime.UtcNow.Ticks - waitStarted) < timeout))
            {
                Service.SavePendingHistory();
                System.Threading.Thread.Sleep(250);
                controller.Refresh();
            }

            controller.Refresh();

            return (controller.Status == ServiceControllerStatus.Running) || (controller.Status == ServiceControllerStatus.Stopped);
        }
    }
}
